//Библиотека Qt.
#include <QtGui>
#include <QX11Info>


//Внутренняя библиотека.
#include "openglwidget.h"
#include "vidframe.h"
#include "videvent.h"


//Глобальные функции-константы.
#define FOURCC(a, b, c, d) (((int)(a) << 0) | ((int)(b) << 8) | ((int)(c) << 16) | ((int)(d) << 24))


//===========================================================================//
//=Заголовок: OpenGLWidget::OpenGLWidget() - public
//=Параметры:
//=Описание: Конструктор.
//===========================================================================//
OpenGLWidget::OpenGLWidget(int index, QWidget *parent) : VidWidget(index, parent)
{
vidFrame = NULL; doubleBuffered = GL_TRUE; isCapturing = false;

setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground);

Display *display = x11Info().display();
if(!display) return;

int error_base, event_base;
if(!glXQueryExtension(display, &error_base, &event_base)) return;

int doublebuffervisual[] = {GLX_RGBA, GLX_DOUBLEBUFFER, None};
XVisualInfo *visualinfo = glXChooseVisual(display, DefaultScreen(display), doublebuffervisual);
if(visualinfo == NULL)
	{
	int singlebuffervisual[] = {GLX_RGBA, None};
	visualinfo = glXChooseVisual(display, DefaultScreen(display), singlebuffervisual);
	if(visualinfo == NULL) return;

	doubleBuffered = false;
}

glxContext = glXCreateContext(display, visualinfo, NULL, GL_TRUE);

connect(this, SIGNAL(needRepaint()), this, SLOT(repaint()), Qt::QueuedConnection);
}


//===========================================================================//
//=Заголовок: VidWidget::WINTYPE OpenGLWidget::getType() - public
//=Параметры:
//=Описание: Функция возврата типа видеоокна.
//===========================================================================//
VidWidget::WINTYPE OpenGLWidget::getType() {return VidWidget::OPENGL;}


//===========================================================================//
//=Заголовок: void OpenGLWidget::resizeEvent() - protected
//=Параметры:
//=Описание: Событие изменения размеров виджета.
//===========================================================================//
void OpenGLWidget::resizeEvent(QResizeEvent *event)
{
Display *display = x11Info().display();
if(!display) {event->accept(); return;}
if(!glxContext) {event->accept(); return;}
glXMakeCurrent(display, winId(), glxContext);
glViewport(0, 0, event->size().width(), event->size().height());
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-5, 5, -5, 5, 2, 12);
gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
glMatrixMode(GL_MODELVIEW);
update();
event->accept();
}


//===========================================================================//
//=Заголовок: void OpenGLWidget::paintEvent() - protected
//=Параметры:
//=Описание: Событие перерисовки виджета.
//===========================================================================//
void OpenGLWidget::paintEvent(QPaintEvent *event)
{
if(!vidFrame) {event->accept(); return;}

if(!isCapturing)
	{
	convertV4L2FmtToGLFmt(vidFrame->getV4L2PixFormat());

	Display *display = x11Info().display();
	if(!display || !glxContext)
		{event->ignore(); vidFrame = NULL; return;}
	glXMakeCurrent(display, winId(), glxContext);

	glClear(GL_COLOR_BUFFER_BIT);
	glViewport(0, 0, width(), height());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-5, 5, -5, 5, 2, 12);
	gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
	glMatrixMode(GL_MODELVIEW);

	isCapturing = true;
}

Display *display = x11Info().display();
if(!display || !glxContext)
	{event->ignore(); vidFrame = NULL; return;}
glXMakeCurrent(display, winId(), glxContext);

glBindTexture(GL_TEXTURE_2D, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, glFormat, vidFrame->getWidth(), vidFrame->getHeight(), 0, glFormat, GL_UNSIGNED_BYTE, vidFrame->getDeinterlacedData());

glDepthFunc(GL_EQUAL);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 1);
glPushMatrix();

glBegin(GL_POLYGON);
glTexCoord2f(1.0, 0.0); glVertex2f(5.0, 5.0);
glTexCoord2f(0.0, 0.0); glVertex2f(-5.0, 5.0);
glTexCoord2f(0.0, 1.0); glVertex2f(-5.0, -5.0);
glTexCoord2f(1.0, 1.0); glVertex2f(5.0, -5.0);
glEnd();

glPopMatrix();
glDisable(GL_TEXTURE_2D);

if(doubleBuffered) glXSwapBuffers(display, winId());
else glFlush();

vidFrame = NULL;

event->accept();
}


//===========================================================================//
//=Заголовок: void OpenGLWidget::customEvent() - protected
//=Параметры:
//=Описание: Событие уровня приложения.
//===========================================================================//
void OpenGLWidget::customEvent(QEvent *event)
{
VidEvent *videvent = (VidEvent *)event;
if(!videvent) return;

vidFrame = videvent->getFrame();
if(!vidFrame) {isCapturing = false; event->accept(); return;}

emit needRepaint();

event->accept();
}


//===========================================================================//
//=Заголовок: void OpenGLWidget::convertV4L2FmtToGLFmt() - private
//=Параметры:
//=Описание: Функция конвертации формата видео из v4l2 в формат OpenGL.
//===========================================================================//
void OpenGLWidget::convertV4L2FmtToGLFmt(int pixfmt)
{
if(pixfmt == FOURCC('R','G','B','1')) {glFormat = GL_LUMINANCE_ALPHA; return;}
if(pixfmt == FOURCC('R','G','B','O')) {glFormat = GL_LUMINANCE_ALPHA; return;}
if(pixfmt == FOURCC('R','G','B','Q')) {glFormat = GL_LUMINANCE_ALPHA; return;}
if(pixfmt == FOURCC('R','G','B','P')) {glFormat = GL_LUMINANCE_ALPHA; return;}
if(pixfmt == FOURCC('R','G','B','R')) {glFormat = GL_LUMINANCE_ALPHA; return;}
if(pixfmt == FOURCC('B','G','R','3')) {glFormat = GL_RGB;             return;}
if(pixfmt == FOURCC('R','G','B','3')) {glFormat = GL_RGB;             return;}
if(pixfmt == FOURCC('R','G','B','4')) {glFormat = GL_RGBA;            return;}
if(pixfmt == FOURCC('B','G','R','4')) {glFormat = GL_RGBA;            return;}
if(pixfmt == FOURCC('G','R','E','Y')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('Y','V','U','9')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('Y','V','1','2')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('Y','U','Y','V')) {glFormat = GL_LUMINANCE_ALPHA; return;}
if(pixfmt == FOURCC('U','Y','V','Y')) {glFormat = GL_LUMINANCE_ALPHA; return;}
if(pixfmt == FOURCC('4','2','2','P')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('4','1','1','P')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('Y','4','1','P')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('N','V','1','2')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('N','V','2','1')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('Y','U','V','9')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('Y','U','1','2')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('Y','Y','U','V')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('H','I','2','4')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('H','M','1','2')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('B','A','8','1')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('M','J','P','G')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('J','P','E','G')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('d','v','s','d')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('M','P','E','G')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('W','N','V','A')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('S','9','1','0')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('P','W','C','1')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('P','W','C','2')) {glFormat = GL_LUMINANCE;       return;}
if(pixfmt == FOURCC('E','6','2','5')) {glFormat = GL_LUMINANCE;       return;}

glFormat = GL_LUMINANCE;
}
